﻿using System;
using Hangfire;
using L._52Abp;
using Yoyo.Abp;
using System.IO;
using System.Linq;
using Abp.Hangfire;
using Yoyo.Abp.FTF;
using Abp.Extensions;
using L._52Abp.Users;
using L._52Abp.Swagger;
using System.Reflection;
using L._52Abp.Security;
using L._52AbpPro.Users;
using L._52Abp.BlobStoring;
using GCT.Foundation.Common;
using GCT.Foundation.Wechat;
using HealthChecks.UI.Client;
using GCT.Foundation.Startup;
using GCT.Foundation.Identity;
using Alipay.AopSdk.AspnetCore;
using Microsoft.OpenApi.Models;
using Castle.Facilities.Logging;
using GCT.Foundation.HealthCheck;
using Newtonsoft.Json.Converters;
using Abp.AspNetCore.SignalR.Hubs;
using GCT.Foundation.Chat.SignalR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using GCT.Foundation.Authorization;
using GCT.Foundation.Configuration;
using GCT.Foundation.IdentityServer;
using HealthChecks.UI.Configuration;
using Abp.AspNetCore.Mvc.Antiforgery;
using Swashbuckle.AspNetCore.SwaggerUI;
using Swashbuckle.AspNetCore.SwaggerGen;
using Microsoft.Extensions.Configuration;
using GCT.Foundation.EntityFrameworkCore;
using GCT.Foundation.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Castle.Services.Logging.SerilogIntegration;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using StackExchange.Redis;

namespace GCT.Foundation.Template
{
    public class Startup
    {
        private const string DefaultCorsPolicyName = "GCT.Foundation.Template";


        private readonly IWebHostEnvironment _env;

        public Startup(IWebHostEnvironment env)
        {
            _env = env;
            Configuration = env.GetAppConfiguration();
            InitWebConsts();
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            FoundationConfigs.Database.SkipDbContextRegistration = true;
            FoundationConfigs.Authorization.SkipAuthorization = true;
            FoundationConfigs.Database.SkipDbSeed = true;

            // mvc/api 服务
            ConfigureMvcService(services);

            // 跨域
            ConfigureCorsService(services);

            // 支付宝
            ConfigureAlipayService(services);

            // 微信
            ConfigureWeChatService(services);

            // IdentityServer4
            ConfigureIdentityServer4Service(services);

            // swagger ui
            ConfigureSwaggerUIService(services);

            // health check ui
            ConfigureHealthCheckService(services);

            // hangfire
            ConfigureHangfireService(services);

            // identity
            ConfigureAuth(services);

            // request localization
            services.Add52AbpRequestLocalization();


            services.Add52AbpBlobStoring(options =>
            {
                options.Containers.ConfigureDefault(container =>
                {
                    if (WebConsts.MinioConfig.IsEnabled)
                    {
                        container.UseMinio(minio =>
                        {
                            minio.EndPoint = WebConsts.MinioConfig.Endpoint;
                            minio.AccessKey = WebConsts.MinioConfig.AccessKey;
                            minio.SecretKey = WebConsts.MinioConfig.SecretKey;
                            minio.BucketName = WebConsts.MinioConfig.BucketName;
                            minio.CreateBucketIfNotExists = true;
                        });
                    }
                    else
                    {
                        var newBasePath = Path.Combine(_env.WebRootPath, "FileUpload");
                        var basePath = Configuration["App:FileManagment:FileSystem:BasePath"] ?? newBasePath;
                        if (!Directory.Exists(basePath))
                        {
                            Console.WriteLine($"{basePath}目录不存在，将使用{newBasePath}目录");
                            basePath = newBasePath;
                        }
                        container.UseFileSystem(fileSystem =>
                        {
                            fileSystem.BasePath = basePath;
                        });
                    }
                });
            });

            // type finder
            services.Add52AbpTypeFinder((options) =>
            {
                // skip find
                options.AddSkip<FoundationDbContext>();
            });

            services.AddTransient<IUserClaimsPrincipalProcessor<User>, UserClaimsPrincipalProcessor>();

            //
            return services.AddGCTAbp<FoundationTemplateWebHostModule>(options =>
            {
                options.AbpOptionsAction = (abpOptions) =>
                {
                    abpOptions.IocManager.IocContainer.AddFacility<LoggingFacility>(f =>
                    {
                        f.LogUsing(new SerilogFactory(Serilog.Log.Logger));
                    });
                };

                options.AbpConfigureAspNetCoreAfter = (s) =>
                {
                };
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app)
        {
            // token的过期时间
            //AppConsts.AccessTokenExpiration = TimeSpan.FromMinutes(10);
            //AppConsts.RefreshTokenExpiration = TimeSpan.FromMinutes(15);
            
            // 基本的中间件
            UseBasic(app);

            // Hangfire
            UseHangfire(app);

            // EndPoint
            app.UseEndpoints(endpoints =>
            {
                // signalr
                endpoints.MapHub<AbpCommonHub>("/signalr");
                endpoints.MapHub<ChatHub>("/signalr-chat");

                // mvc/webapi
                endpoints.MapDefaultControllerRoute();
                //endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}");
                //endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
                endpoints.MapRazorPages();

                // 健康检查
                if (Configuration.HealthChecksEnabled())
                {
                    endpoints.MapHealthChecks("/healthz", new HealthCheckOptions()
                    {
                        Predicate = healthCheckRegistration => true,
                        ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
                    });
                    endpoints.MapHealthChecksUI();
                }
            });

            // 注册swagger
            UseSwaggerUI(app);
   
            ////
            //if (Configuration.HealthChecksUIEnabled())
            //{
            //    app.UseHealthChecksUI();
            //}
        }

        #region ConfigureServices

        /// <summary>
        /// 配置MVC服务
        /// </summary>
        /// <param name="services"></param>
        protected virtual void ConfigureMvcService(IServiceCollection services)
        {

            if (WebConsts.RedisDatabaseId >= 0)
            {
                // 启用数据保护多设备共享
                services.AddDataProtection()
                    .SetApplicationName(DefaultCorsPolicyName)
                    .PersistKeysToStackExchangeRedis(
                        ConnectionMultiplexer.Connect(WebConsts.RedisConnectionStr),
                        $"{DefaultCorsPolicyName}-data-keys");
            }

            services.AddMvc(options =>
                {
                    options.Filters.Add(new AbpAutoValidateAntiforgeryTokenAttribute());
                })
                // razor pages options
                .AddRazorPagesOptions((options) =>
                {
                })
#if DEBUG
                .AddRazorRuntimeCompilation()
#endif
                .AddNewtonsoftJson(options =>
                {
                    // 将枚举信息转换为字符串。
                    options.SerializerSettings.Converters.Add(new StringEnumConverter());
                });

            var signalrServerBuilder = services.AddSignalR(options =>
            {
                options.EnableDetailedErrors = true;
            }).AddNewtonsoftJsonProtocol();

            if (FoundationTemplateConfigs.SignalRRedis.SignalRDatabaseId >= 0)
            {
                signalrServerBuilder
                    .AddStackExchangeRedis($"{FoundationTemplateConfigs.SignalRRedis.ConnectionSignalRString}," +
                                           $"defaultDatabase={FoundationTemplateConfigs.SignalRRedis.SignalRDatabaseId}");
            }

            services.AddHttpClient();
        }

        /// <summary>
        /// 配置跨域
        /// </summary>
        /// <param name="services"></param>
        protected virtual void ConfigureCorsService(IServiceCollection services)
        {
            // 在appsettings.json中可以包含多个跨域地址，由逗号隔开。
            var corsOrigins = Configuration["App:CorsOrigins"]
                .Split(",", StringSplitOptions.RemoveEmptyEntries)
                .Select(o => o.RemovePostFix("/"))
                .Distinct()
                .ToArray();

            services.AddCors(
                options => options.AddPolicy(
                    DefaultCorsPolicyName,
                    builder => builder
                        .WithOrigins(corsOrigins)
                        .SetIsOriginAllowedToAllowWildcardSubdomains()
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowCredentials()
                )
            );
        }

        /// <summary>
        /// 注册支付宝服务
        /// </summary>
        /// <param name="services"></param>
        protected virtual void ConfigureAlipayService(IServiceCollection services)
        {
            var alipayOptions = Configuration.GetSection("Pay:Alipay").Get<AlipayOptions>();

            services.AddYoYoAlipay(() => alipayOptions, config =>
            {
                config ??= new FTFConfig();
                config.QRCodeGenErrorImageFullPath = Path.Combine(
                    _env.WebRootPath,
                    "imgs",
                    "pay",
                    "alipay_error.png"
                    );
                config.QRCodeIconFullPath = Path.Combine(
                    _env.WebRootPath,
                    "imgs",
                    "pay",
                    "alipay.png"
                    );
            });
        }

        /// <summary>
        /// 注册微信服务
        /// </summary>
        /// <param name="services"></param>
        protected virtual void ConfigureWeChatService(IServiceCollection services)
        {
            services.AddWechat(Configuration);
        }

        /// <summary>
        /// 注册 IdentityServer4 服务
        /// </summary>
        /// <param name="services"></param>
        protected virtual void ConfigureIdentityServer4Service(IServiceCollection services)
        {
            if (bool.Parse(Configuration["IdentityServer:IsEnabled"]))
            {
                IdentityServerRegistrar.Register(services, Configuration);
            }
        }

        /// <summary>
        /// 注册 SwaggerUI 服务
        /// </summary>
        /// <param name="services"></param>
        protected virtual void ConfigureSwaggerUIService(IServiceCollection services)
        {
            //Swagger -启用此行以及Configure方法中的相关行，以启用Swagger UI
            services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo
                {
                    Title = "52ABP-PRO API",
                    Version = "v1",
                    Description = "52ABP-PRO 的动态WEBAPI管理端，可以进行测试和调用API。",
                    TermsOfService = new Uri("https://code.52abp.com/52abp/pro/52ABP-Enterprise"),
                    Contact = new OpenApiContact
                    {
                        Name = "52abp.com",
                        Email = "ltm@ddxc.org",
                        Url = new Uri("https://www.52abp.com/")
                    },
                });

                //使用相对路径获取应用程序所在目录
                options.DocInclusionPredicate((docName, description) => true);
                // 支持非body内容中的枚举
                options.UseEnumParameterFilter();
                // 对应client枚举转为字符串对应值
                options.UseEnumSchemaFilter();
                options.UseOperationIdFilter();
                options.UseOperationFilter();
                options.CustomDefaultSchemaIdSelector();

                // 排序规则
                options.OrderActionsBy(x => x.RelativePath);
                // 使用 CamelCase 命名规则处理枚举
                options.DescribeAllParametersInCamelCase();

                // 配置注释
                ConfigureSwaggerUIDoc(options);
            });

            // 使用 newtonsoft.json 做swagger的序列化工具
            services.AddSwaggerGenNewtonsoftSupport();
        }

        /// <summary>
        /// 配置类库的注释,开发时使用
        /// </summary>
        /// <param name="options"> </param>
        protected virtual void ConfigureSwaggerUIDoc(SwaggerGenOptions options)
        {
#if DEBUG
            var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);

            // Foundation.Template.Application层
            var gctFileName = typeof(FoundationTemplateApplicationModule).Assembly.GetName().Name + ".XML";
            var gctFilePath = Path.Combine(basePath, gctFileName);
            options.IncludeXmlComments(gctFilePath);

#endif
        }

        /// <summary>
        /// 注册健康检查
        /// </summary>
        /// <param name="services"></param>
        protected virtual void ConfigureHealthCheckService(IServiceCollection services)
        {
            // 是否启用健康检查
            if (!Configuration.HealthChecksEnabled())
            {
                return;
            }
            services.AddYoyoCmsHealthCheck();

            // 是否启用健康检查UI
            if (!Configuration.HealthChecksUIEnabled())
            {
                return;
            }

            var healthCheckUISection = Configuration.HealthChecksUI();
            services.Configure<Settings>(settings =>
            {
                healthCheckUISection.Bind(settings, c => c.BindNonPublicProperties = true);
            });
            services.AddHealthChecksUI().AddInMemoryStorage();
        }

        /// <summary>
        /// 注册Hangfire
        /// </summary>
        /// <param name="services"></param>
        protected virtual void ConfigureHangfireService(IServiceCollection services)
        {
            services.AddHangfire(config =>
            {
                config.UseSqlServerStorage(Configuration.GetConnectionString("Default"));
            });
        }

        /// <summary>
        /// 注册认证
        /// </summary>
        /// <param name="services"></param>
        protected virtual void ConfigureAuth(IServiceCollection services)
        {
            services.AddAppIdentity();
            services.AddAppAuthentication(Configuration);
        }

        #endregion ConfigureServices

        #region Configure

        /// <summary>
        ///
        /// </summary>
        /// <param name="app"></param>
        /// <param name="env"></param>
        protected virtual void UseBasic(IApplicationBuilder app)
        {
            // 初始化ABP框架
            app.UseGCTAbp(options => { });

     

            if (_env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            
            // 本地化
            app.Use52AbpRequestLocalization();

            // 允许iframe嵌入资源
            app.UseSecurityHeaders(builder =>
            {
                builder.AddCustomHeader("X-Frame-Options", "AllowAll");
            });

            // 静态文件
            app.UseStaticFiles(new StaticFileOptions
            {
                // 不支持的文件类型设置为 application/octet-stream
                ServeUnknownFileTypes = true,
                DefaultContentType = "application/octet-stream"
            });

            // 路由
            app.UseRouting();

            // 跨域
            app.UseCors(DefaultCorsPolicyName);

            // 身份验证
            app.UseAuthentication();

            // jwt
            app.UseJwtTokenMiddleware();

            // 授权
            app.UseAuthorization();

            // FastReport
            app.UseFastReport();

            // 微信
            app.UseWechat(_env);
        }

        /// <summary>
        /// 启用Hangfire
        /// </summary>
        /// <param name="app"></param>
        protected virtual void UseHangfire(IApplicationBuilder app)
        {
            // TODO: 判断是否启用 HangfireDashboard

            //配置服务最大重试次数值
            GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute { Attempts = 5 });
            //设置Hangfire服务可选参数
            var jobOptions = new BackgroundJobServerOptions
            {
                //wait all jobs performed when BackgroundJobServer shutdown.
                ShutdownTimeout = TimeSpan.FromMinutes(30),
                Queues = new[] { "default", "jobs" }, //队列名称，只能为小写
                WorkerCount = 3, //Environment.ProcessorCount * 5, //并发任务数 Math.Max(Environment.ProcessorCount, 20)
                ServerName = "yoyosoft.hangfire",
            };

            //启用Hangfire仪表盘和服务器(支持使用Hangfire而不是默认的后台作业管理器)
            app.UseHangfireDashboard("/hangfire", new DashboardOptions
            {
                Authorization = new[]
                {
                        new AbpHangfireAuthorizationFilter(YoyoSoftPermissionNames.Pages_Administration_HangfireDashboard)
                    }
            });

            app.UseHangfireServer(jobOptions);
        }

        /// <summary>
        /// SwaggerUI
        /// </summary>
        /// <param name="app"></param>
        protected virtual void UseSwaggerUI(IApplicationBuilder app)
        {
            // 使中间件能够作为JSON端点提供生成的Swagger
            app.UseSwagger();
            // 使中间件能够提供swagger-ui(HTML、JS、CSS等)
            app.UseSwaggerUI(options =>
            {
                // SwaggerEndPoint
                options.SwaggerEndpoint(
                    Configuration["App:SwaggerEndPoint"],
                    "Foundation Template API V1"
                    );
                options.EnableDeepLinking();
                options.DocExpansion(DocExpansion.None);

                options.IndexStream = () => Assembly.GetExecutingAssembly()
                    .GetManifestResourceStream("GCT.Foundation.wwwroot.pages.swagger.index.html");
            }); // URL: /swagger

            // 触发
            app.UseSwaggerProvider((swaggerProvider) =>
            {
                swaggerProvider.GetSwagger("v1", null, "");
            });
        }

        #endregion Configure

        private void InitWebConsts()
        {
            WebConsts.MinioConfig.IsEnabled = Configuration.GetValue<bool>("App:FileManagment:Minio:IsEnabled");
            WebConsts.MinioConfig.AccessKey = Configuration["App:FileManagment:Minio:AccessKey"];
            WebConsts.MinioConfig.SecretKey = Configuration["App:FileManagment:Minio:SecretKey"];
            WebConsts.MinioConfig.Endpoint = Configuration["App:FileManagment:Minio:Endpoint"];
            WebConsts.MinioConfig.BucketName = Configuration["App:FileManagment:Minio:BucketName"];

            // Redis
            WebConsts.RedisDatabaseId = Configuration.GetValue<int>("Cache:Redis:DatabaseId");
            WebConsts.RedisConnectionStr = Configuration["Cache:Redis:ConnectionString"];

            // SignalR 
            FoundationTemplateConfigs.SignalRRedis.ConnectionSignalRString =
                Configuration["Cache:Redis:ConnectionSignalRString"];
            FoundationTemplateConfigs.SignalRRedis.SignalRDatabaseId =
                Configuration.GetValue<int>("Cache:Redis:SignalRDatabaseId");
        }
    }
}
